package com.nanxun.config;


import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nanxun.Impl.UserDetailsServiceImpl;
import com.nanxun.constant.AuthConstants;
import com.nanxun.constant.BusinessEnum;
import com.nanxun.constant.HttpConstants;
import com.nanxun.model.LoginResult;
import com.nanxun.model.Result;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.io.IOException;
import java.io.PrintWriter;
import java.time.Duration;
import java.util.UUID;


/**
 * spring security安全框架配置类
 * */

@Configuration
public class AuthSecurityConfig  {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

        @Bean
        public  SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
         /*   httpSecurity.userDetailsService(userDetailsService);
            //关闭跨域请求伪造
            httpSecurity.cors(cors-> cors.disable());
            //关闭跨站请求
            httpSecurity.csrf(csrf-> csrf.disable());
            //关闭session使用策略
            httpSecurity.sessionManagement(session-> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
            //配置登录信息
            httpSecurity.formLogin(formLogin-> {
                formLogin.loginProcessingUrl(AuthConstants.LOGIN_URL)//设置登录URL
                        .successHandler(authenticationSuccessHandler())//设置登录成功处理器
                        .failureHandler(authenticationFailureHandler());//设置登录失败处理器
            });
            //配置登出信息
            httpSecurity.logout(logout-> {
               logout.logoutUrl(AuthConstants.LOGOUT_URL)//设置登出URL
                       .logoutSuccessHandler(LogoutSuccessHandler());//设置登出成功处理器
            });
            //要求所有请求都需要进行身份的认证
            httpSecurity.authorizeHttpRequests(authorizeHttpRequests->{
                 authorizeHttpRequests.anyRequest().authenticated();
            });
                return httpSecurity.build();*/
            /**
             * 设置security安全框架走自己的认证
             * */
            httpSecurity.userDetailsService(userDetailsService)
                    .cors(cors->{ cors.disable();}) //关闭跨站请求伪造
                    .csrf(csrf->{ csrf.disable();})  //关闭跨域请求
                    //关闭session使用策略
                    .sessionManagement(session->{ session.sessionCreationPolicy(SessionCreationPolicy.STATELESS);})
                    //配置登录信息
                    .formLogin(formLogin-> {
                        formLogin.loginProcessingUrl(AuthConstants.LOGIN_URL)//设置登录URL
                                .successHandler(authenticationSuccessHandler())//设置登录成功处理器
                                .failureHandler(authenticationFailureHandler());//设置登录失败处理器
                    })
                    //配置登出信息
                    .logout(logout-> {
                        logout.logoutUrl(AuthConstants.LOGOUT_URL)//设置登出URL
                                .logoutSuccessHandler(LogoutSuccessHandler());//设置登出成功处理器
                    })
                    //要求所有请求都需要进行身份的认证
                    .authorizeHttpRequests(authorizeHttpRequests->{
                        authorizeHttpRequests.anyRequest().authenticated();
                    });
            return   httpSecurity.build();
        }

        /**
         * 登录成功处理器
         * */
        @Bean
        public AuthenticationSuccessHandler authenticationSuccessHandler(){
            return (request, response, authentication) -> {
                //设置响应头信息
                response.setContentType(HttpConstants.CONTENT_TYPE);
                response.setCharacterEncoding(HttpConstants.UTF_8);

                //使用uuid来当做token
                String token = UUID.randomUUID().toString();
                //从security框架中获取认证用户对象(securityUser)并转换为json格式的字符串
                String userJsonStr = JSONObject.toJSONString(authentication.getPrincipal());
                //将token当作key，认证用户对象的json格式的字符串当前的value存放到redis中
                stringRedisTemplate.opsForValue().set(AuthConstants.LOGIN_TOKEN_PREFIX+token,userJsonStr,
                        Duration.ofSeconds(AuthConstants.TOKEN_TIME));
                //封装一个登录统一结果对象
                LoginResult loginResult = new LoginResult(token,AuthConstants.TOKEN_TIME);
                //创建一个响应结果对象
                Result<Object> result = Result.success(loginResult);

                //返回结果
                ObjectMapper objectMapper = new ObjectMapper();
                String s = objectMapper.writeValueAsString(result);
                PrintWriter writer = response.getWriter();
                writer.write(s);
                writer.flush();
                writer.close();

            };
        }
        /**
         * 登录失败处理器
         * */
        @Bean
        public AuthenticationFailureHandler authenticationFailureHandler(){
                return (request, response, exception) -> {
                    //设置响应头信息
                    response.setContentType(HttpConstants.CONTENT_TYPE);
                    response.setCharacterEncoding(HttpConstants.UTF_8);

                    //创建统一响应结果对象
                    Result<Object> result = new Result<>();
                    result.setCode(BusinessEnum.OPERATION_FAIL.getCode());
                    if (exception instanceof BadCredentialsException){
                        result.setMsg("用户名或密码错误");
                    }else if(exception instanceof UsernameNotFoundException){
                        result.setMsg("用户不存在");
                    }else if(exception instanceof AccountExpiredException){
                        result.setMsg("账号异常，请联系管理员");
                    }else if(exception instanceof AccountStatusException){
                        result.setMsg("账号异常，请联系管理员");
                    }else if (exception instanceof InternalAuthenticationServiceException){
                        result.setMsg(exception.getMessage());
                    }else {
                        result.setMsg(BusinessEnum.OPERATION_FAIL.getDesc());
                    }

                    //返回结果
                    ObjectMapper objectMapper = new ObjectMapper();
                    String s = objectMapper.writeValueAsString(result);
                    PrintWriter writer = response.getWriter();
                    writer.write(s);
                    writer.flush();
                    writer.close();
                };
        }

        /**
         * 登出成功处理器
         * */
        @Bean
        public LogoutSuccessHandler LogoutSuccessHandler(){
            return (request, response, authentication) -> {
                //设置响应头信息
                response.setContentType(HttpConstants.CONTENT_TYPE);
                response.setCharacterEncoding(HttpConstants.UTF_8);
                //从请求头中获取token
                String authorization = request.getHeader(AuthConstants.AUTHORIZATION);
                String token = authorization.replaceFirst(AuthConstants.BEARER, "");
                //将当前token从redis中删除
                stringRedisTemplate.delete(AuthConstants.LOGIN_TOKEN_PREFIX+token);

                //创建统一响应结果对象
                Result<Object> result = Result.success(null);
                //返回结果
                ObjectMapper objectMapper = new ObjectMapper();
                String s = objectMapper.writeValueAsString(result);
                PrintWriter writer = response.getWriter();
                writer.write(s);
                writer.flush();
                writer.close();
            };
        }
        /**
         * 密码加密器
         * */
        @Bean
        public BCryptPasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
        }
}
